﻿// This is an open source non-commercial project. Dear PVS-Studio, please check it.
// PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com

// ReSharper disable CheckNamespace
// ReSharper disable ClassNeverInstantiated.Global
// ReSharper disable CommentTypo
// ReSharper disable IdentifierTypo
// ReSharper disable InconsistentNaming
// ReSharper disable StringLiteralTypo
// ReSharper disable UnusedParameter.Local

/*
 * Ars Magna project, http://arsmagna.ru
 */

#region Using directives

using System;
using System.Collections.Generic;
using System.Text;

using AM.Reporting.Utils;

using System.Globalization;

using AM.Reporting.Data;

#endregion

#nullable enable

namespace AM.Reporting.Functions
{
    /// <summary>
    /// Contains standard functions registered in the "Data" window.
    /// </summary>
    public static class StdFunctions
    {
        #region Math functions

        /// <summary>
        /// Returns the larger of two 32-bit signed integers.
        /// </summary>
        /// <param name="val1">The first of two values to compare.</param>
        /// <param name="val2">The second of two values to compare.</param>
        /// <returns>Parameter val1 or val2, whichever is larger.</returns>
        public static int Maximum (int val1, int val2)
        {
            return Math.Max (val1, val2);
        }

        /// <summary>
        /// Returns the larger of two 64-bit signed integers.
        /// </summary>
        /// <param name="val1">The first of two values to compare.</param>
        /// <param name="val2">The second of two values to compare.</param>
        /// <returns>Parameter val1 or val2, whichever is larger.</returns>
        public static long Maximum (long val1, long val2)
        {
            return Math.Max (val1, val2);
        }

        /// <summary>
        /// Returns the larger of two single-precision floating-point numbers.
        /// </summary>
        /// <param name="val1">The first of two values to compare.</param>
        /// <param name="val2">The second of two values to compare.</param>
        /// <returns>Parameter val1 or val2, whichever is larger.</returns>
        public static float Maximum (float val1, float val2)
        {
            return Math.Max (val1, val2);
        }

        /// <summary>
        /// Returns the larger of two double-precision floating-point numbers.
        /// </summary>
        /// <param name="val1">The first of two values to compare.</param>
        /// <param name="val2">The second of two values to compare.</param>
        /// <returns>Parameter val1 or val2, whichever is larger.</returns>
        public static double Maximum (double val1, double val2)
        {
            return Math.Max (val1, val2);
        }

        /// <summary>
        /// Returns the larger of two decimal numbers.
        /// </summary>
        /// <param name="val1">The first of two values to compare.</param>
        /// <param name="val2">The second of two values to compare.</param>
        /// <returns>Parameter val1 or val2, whichever is larger.</returns>
        public static decimal Maximum (decimal val1, decimal val2)
        {
            return Math.Max (val1, val2);
        }

        /// <summary>
        /// Returns the smaller of two 32-bit signed integers.
        /// </summary>
        /// <param name="val1">The first of two values to compare.</param>
        /// <param name="val2">The second of two values to compare.</param>
        /// <returns>Parameter val1 or val2, whichever is smaller.</returns>
        public static int Minimum (int val1, int val2)
        {
            return Math.Min (val1, val2);
        }

        /// <summary>
        /// Returns the smaller of two 64-bit signed integers.
        /// </summary>
        /// <param name="val1">The first of two values to compare.</param>
        /// <param name="val2">The second of two values to compare.</param>
        /// <returns>Parameter val1 or val2, whichever is smaller.</returns>
        public static long Minimum (long val1, long val2)
        {
            return Math.Min (val1, val2);
        }

        /// <summary>
        /// Returns the smaller of two single-precision floating-point numbers.
        /// </summary>
        /// <param name="val1">The first of two values to compare.</param>
        /// <param name="val2">The second of two values to compare.</param>
        /// <returns>Parameter val1 or val2, whichever is smaller.</returns>
        public static float Minimum (float val1, float val2)
        {
            return Math.Min (val1, val2);
        }

        /// <summary>
        /// Returns the smaller of two double-precision floating-point numbers.
        /// </summary>
        /// <param name="val1">The first of two values to compare.</param>
        /// <param name="val2">The second of two values to compare.</param>
        /// <returns>Parameter val1 or val2, whichever is smaller.</returns>
        public static double Minimum (double val1, double val2)
        {
            return Math.Min (val1, val2);
        }

        /// <summary>
        /// Returns the smaller of two decimal numbers.
        /// </summary>
        /// <param name="val1">The first of two values to compare.</param>
        /// <param name="val2">The second of two values to compare.</param>
        /// <returns>Parameter val1 or val2, whichever is smaller.</returns>
        public static decimal Minimum (decimal val1, decimal val2)
        {
            return Math.Min (val1, val2);
        }

        #endregion

        #region Text functions

        /// <summary>
        /// Returns an integer value representing the character code corresponding to a character.
        /// </summary>
        /// <param name="c">Character to convert.</param>
        /// <returns>The character code.</returns>
        public static int Asc (char c)
        {
            return (int)c;
        }

        /// <summary>
        /// Returns the character associated with the specified character code.
        /// </summary>
        /// <param name="i">Character code to convert.</param>
        /// <returns>The character.</returns>
        public static char Chr (int i)
        {
            return (char)i;
        }

        /// <summary>
        /// Inserts a specified string at a specified index position in the original string.
        /// </summary>
        /// <param name="s">The original string.</param>
        /// <param name="startIndex">The index position of the insertion.</param>
        /// <param name="value">The string to insert.</param>
        /// <returns>A new string.</returns>
        public static string Insert (string s, int startIndex, string value)
        {
            return s == null ? "" : s.Insert (startIndex, value);
        }

        /// <summary>
        /// Gets the number of characters in a string.
        /// </summary>
        /// <param name="s">The original string.</param>
        /// <returns>The number of characters.</returns>
        public static int Length (string s)
        {
            return s == null ? 0 : s.Length;
        }

        /// <summary>
        /// Converts a specified string to lowercase.
        /// </summary>
        /// <param name="s">The string to convert.</param>
        /// <returns>A string in lowercase.</returns>
        public static string LowerCase (string s)
        {
            return s == null ? "" : s.ToLower();
        }

        /// <summary>
        /// Right-aligns the characters in a string, padding with spaces on the left for a specified total length.
        /// </summary>
        /// <param name="s">The original string.</param>
        /// <param name="totalWidth">The number of characters in the resulting string.</param>
        /// <returns>Right-aligned string, padded on the left with spaces.</returns>
        public static string PadLeft (string s, int totalWidth)
        {
            return s == null ? "" : s.PadLeft (totalWidth);
        }

        /// <summary>
        /// Right-aligns the characters in a string, padding on the left with a specified character
        /// for a specified total length.
        /// </summary>
        /// <param name="s">The original string.</param>
        /// <param name="totalWidth">The number of characters in the resulting string.</param>
        /// <param name="paddingChar">A padding character.</param>
        /// <returns>Right-aligned string, padded on the left with padding characters.</returns>
        public static string PadLeft (string s, int totalWidth, char paddingChar)
        {
            return s == null ? "" : s.PadLeft (totalWidth, paddingChar);
        }

        /// <summary>
        /// Left-aligns the characters in a string, padding with spaces on the right, for a specified total length.
        /// </summary>
        /// <param name="s">The original string.</param>
        /// <param name="totalWidth">The number of characters in the resulting string.</param>
        /// <returns>Left-aligned string, padded on the right with spaces.</returns>
        public static string PadRight (string s, int totalWidth)
        {
            return s == null ? "" : s.PadRight (totalWidth);
        }

        /// <summary>
        /// Left-aligns the characters in a string, padding on the right with a specified character,
        /// for a specified total length.
        /// </summary>
        /// <param name="s">The original string.</param>
        /// <param name="totalWidth">The number of characters in the resulting string.</param>
        /// <param name="paddingChar">A padding character.</param>
        /// <returns>Left-aligned string, padded on the right with padding characters.</returns>
        public static string PadRight (string s, int totalWidth, char paddingChar)
        {
            return s == null ? "" : s.PadRight (totalWidth, paddingChar);
        }

        /// <summary>
        /// Converts the specified string to titlecase.
        /// </summary>
        /// <param name="s">The string to convert.</param>
        /// <returns>A new string.</returns>
        public static string TitleCase (string s)
        {
            return s == null ? "" : CultureInfo.CurrentCulture.TextInfo.ToTitleCase (s);
        }

        /// <summary>
        /// Deletes all the characters from a string beginning at a specified position.
        /// </summary>
        /// <param name="s">The original string.</param>
        /// <param name="startIndex">The position to begin deleting characters.</param>
        /// <returns>A new string.</returns>
        public static string Remove (string s, int startIndex)
        {
            return s == null ? "" : s.Remove (startIndex);
        }

        /// <summary>
        /// Deletes a specified number of characters from a string beginning at a specified position.
        /// </summary>
        /// <param name="s">The original string.</param>
        /// <param name="startIndex">The position to begin deleting characters.</param>
        /// <param name="count">The number of characters to delete.</param>
        /// <returns>A new string.</returns>
        public static string Remove (string s, int startIndex, int count)
        {
            return s == null ? "" : s.Remove (startIndex, count);
        }

        /// <summary>
        /// Replaces all occurrences of a specified string in the original string, with another specified string.
        /// </summary>
        /// <param name="s">The original string.</param>
        /// <param name="oldValue">A string to be replaced.</param>
        /// <param name="newValue">A string to replace all occurrences of oldValue.</param>
        /// <returns>A new string.</returns>
        public static string Replace (string s, string oldValue, string newValue)
        {
            return s == null ? "" : s.Replace (oldValue, newValue);
        }

        /// <summary>
        /// Retrieves a substring from the original string, starting at a specified character position.
        /// </summary>
        /// <param name="s">The original string.</param>
        /// <param name="startIndex">The starting character position of a substring.</param>
        /// <returns>A new string.</returns>
        public static string Substring (string s, int startIndex)
        {
            if (s != null && startIndex < s.Length)
            {
                return s.Substring (startIndex);
            }

            return "";
        }

        /// <summary>
        /// Retrieves a substring from the original string, starting at a specified character position,
        /// with a specified length.
        /// </summary>
        /// <param name="s">The original string.</param>
        /// <param name="startIndex">The starting character position of a substring.</param>
        /// <param name="length">The number of characters in the substring.</param>
        /// <returns>A new string.</returns>
        public static string Substring (string s, int startIndex, int length)
        {
            if (s != null && startIndex < s.Length)
            {
                if (startIndex + length < s.Length)
                {
                    return s.Substring (startIndex, length);
                }
                else
                {
                    return s.Substring (startIndex);
                }
            }

            return "";
        }

        /// <summary>
        /// Removes all occurrences of white space characters from the beginning and end of the original string.
        /// </summary>
        /// <param name="s">The original string.</param>
        /// <returns>A new string.</returns>
        public static string Trim (string s)
        {
            return s == null ? "" : s.Trim();
        }

        /// <summary>
        /// Converts a specified string to uppercase.
        /// </summary>
        /// <param name="s">The string to convert.</param>
        /// <returns>A string in uppercase.</returns>
        public static string UpperCase (string s)
        {
            return s == null ? "" : s.ToUpper();
        }

        #endregion

        #region Date & Time functions

        /// <summary>
        /// Adds the specified number of days to the original date.
        /// </summary>
        /// <param name="date">The original date.</param>
        /// <param name="value">A number of whole and fractional days.</param>
        /// <returns>A new DateTime value.</returns>
        public static DateTime AddDays (DateTime date, double value)
        {
            return date.AddDays (value);
        }

        /// <summary>
        /// Adds the specified number of hours to the original date.
        /// </summary>
        /// <param name="date">The original date.</param>
        /// <param name="value">A number of whole and fractional hours.</param>
        /// <returns>A new DateTime value.</returns>
        public static DateTime AddHours (DateTime date, double value)
        {
            return date.AddHours (value);
        }

        /// <summary>
        /// Adds the specified number of minutes to the original date.
        /// </summary>
        /// <param name="date">The original date.</param>
        /// <param name="value">A number of whole and fractional minutes.</param>
        /// <returns>A new DateTime value.</returns>
        public static DateTime AddMinutes (DateTime date, double value)
        {
            return date.AddMinutes (value);
        }

        /// <summary>
        /// Adds the specified number of months to the original date.
        /// </summary>
        /// <param name="date">The original date.</param>
        /// <param name="value">A number of months.</param>
        /// <returns>A new DateTime value.</returns>
        public static DateTime AddMonths (DateTime date, int value)
        {
            return date.AddMonths (value);
        }

        /// <summary>
        /// Adds the specified number of seconds to the original date.
        /// </summary>
        /// <param name="date">The original date.</param>
        /// <param name="value">A number of whole and fractional seconds.</param>
        /// <returns>A new DateTime value.</returns>
        public static DateTime AddSeconds (DateTime date, double value)
        {
            return date.AddSeconds (value);
        }

        /// <summary>
        /// Adds the specified number of years to the original date.
        /// </summary>
        /// <param name="date">The original date.</param>
        /// <param name="value">A number of years.</param>
        /// <returns>A new DateTime value.</returns>
        public static DateTime AddYears (DateTime date, int value)
        {
            return date.AddYears (value);
        }

        /// <summary>
        /// Subtracts the specified date and time from the original date.
        /// </summary>
        /// <param name="date1">The original date.</param>
        /// <param name="date2">The date and time to subtract.</param>
        /// <returns>A TimeSpan interval between two dates.</returns>
        public static TimeSpan DateDiff (DateTime date1, DateTime date2)
        {
            return date1.Subtract (date2);
        }

        /// <summary>
        /// Initializes a new instance of the DateTime.
        /// </summary>
        /// <param name="year">The year.</param>
        /// <param name="month">The month.</param>
        /// <param name="day">The day.</param>
        /// <returns>A new DateTime value.</returns>
        public static DateTime DateSerial (int year, int month, int day)
        {
            return new DateTime (year, month, day);
        }

        /// <summary>
        /// Gets the day of the month.
        /// </summary>
        /// <param name="date">The date value.</param>
        /// <returns>The day component.</returns>
        public static int Day (DateTime date)
        {
            return date.Day;
        }

        /// <summary>
        /// Gets the localized name of the day of the week.
        /// </summary>
        /// <param name="date">The date value.</param>
        /// <returns>The name of the day of the week.</returns>
        public static string DayOfWeek (DateTime date)
        {
            return date.ToString ("dddd");
        }

        /// <summary>
        /// Gets the day of the year.
        /// </summary>
        /// <param name="date">The date value.</param>
        /// <returns>The day of the year.</returns>
        public static int DayOfYear (DateTime date)
        {
            return date.DayOfYear;
        }

        /// <summary>
        /// Returns the number of days in the specified month and year.
        /// </summary>
        /// <param name="year">The year.</param>
        /// <param name="month">The month.</param>
        /// <returns>The number of days in month for the specified year.</returns>
        public static int DaysInMonth (int year, int month)
        {
            return DateTime.DaysInMonth (year, month);
        }

        /// <summary>
        /// Gets the hour component of the date.
        /// </summary>
        /// <param name="date">The date.</param>
        /// <returns>The hour component.</returns>
        public static int Hour (DateTime date)
        {
            return date.Hour;
        }

        /// <summary>
        /// Gets the minute component of the date.
        /// </summary>
        /// <param name="date">The date.</param>
        /// <returns>The minute component.</returns>
        public static int Minute (DateTime date)
        {
            return date.Minute;
        }

        /// <summary>
        /// Gets the month component of the date.
        /// </summary>
        /// <param name="date">The date.</param>
        /// <returns>The month component.</returns>
        public static int Month (DateTime date)
        {
            return date.Month;
        }

        /// <summary>
        /// Gets the localized month name.
        /// </summary>
        /// <param name="month">The month number.</param>
        /// <returns>The month name.</returns>
        public static string MonthName (int month)
        {
            return new DateTime (2000, month, 1).ToString ("MMMM");
        }

        /// <summary>
        /// Gets the seconds component of the date.
        /// </summary>
        /// <param name="date">The date.</param>
        /// <returns>The seconds component.</returns>
        public static int Second (DateTime date)
        {
            return date.Second;
        }

        /// <summary>
        /// Gets the week of the year.
        /// </summary>
        /// <param name="date">The date value.</param>
        /// <returns>The week of the year.</returns>
        public static int WeekOfYear (DateTime date)
        {
            var rule = CultureInfo.CurrentCulture.DateTimeFormat.CalendarWeekRule;
            var day = CultureInfo.CurrentCulture.DateTimeFormat.FirstDayOfWeek;
            return CultureInfo.CurrentCulture.Calendar.GetWeekOfYear (date, rule, day);
        }

        /// <summary>
        /// Gets the year component of the date.
        /// </summary>
        /// <param name="date">The date.</param>
        /// <returns>The year component.</returns>
        public static int Year (DateTime date)
        {
            return date.Year;
        }

        #endregion

        #region Formatting

        /// <summary>
        /// Replaces the format item in a specified String with the text equivalent of the value of a
        /// corresponding Object instance in a specified array.
        /// </summary>
        /// <param name="format">A String containing zero or more format items.</param>
        /// <param name="args">An Object array containing zero or more objects to format.</param>
        /// <returns>A copy of format in which the format items have been replaced by the String equivalent of the corresponding instances of Object in args.</returns>
        public static string Format (string format, params object[] args)
        {
            if (args != null)
            {
                for (var i = 0; i < args.Length; i++)
                {
                    if (args[i] is Variant)
                    {
                        args[i] = ((Variant)args[i]).Value;
                    }
                }
            }

            return string.Format (format, args);
        }

        /// <summary>
        /// Returns a string formatted as a currency value.
        /// </summary>
        /// <param name="value">The value to format.</param>
        /// <returns>The formatted string.</returns>
        public static string FormatCurrency (object value)
        {
            if (value is Variant variant)
            {
                value = variant.Value;
            }

            return string.Format ("{0:c}", value);
        }

        /// <summary>
        /// Returns a string formatted as a currency value with specified number of decimal digits.
        /// </summary>
        /// <param name="value">The value to format.</param>
        /// <param name="decimalDigits">Number of decimal digits.</param>
        /// <returns>The formatted string.</returns>
        public static string FormatCurrency (object value, int decimalDigits)
        {
            if (value is Variant variant)
            {
                value = variant.Value;
            }

            var info = CultureInfo.CurrentCulture.NumberFormat.Clone() as NumberFormatInfo;
            info.CurrencyDecimalDigits = decimalDigits;
            return string.Format (info, "{0:c}", value);
        }

        /// <summary>
        /// Returns a string formatted as a date/time value.
        /// </summary>
        /// <param name="value">The value to format.</param>
        /// <returns>The formatted string.</returns>
        public static string FormatDateTime (DateTime value)
        {
            var format = "G";
            if (value.TimeOfDay.Ticks == value.Ticks)
            {
                format = "T";
            }
            else if (value.TimeOfDay.Ticks == 0)
            {
                format = "d";
            }

            return value.ToString (format, null);
        }

        /// <summary>
        /// Returns a string formatted as a date/time value.
        /// </summary>
        /// <param name="value">The value to format.</param>
        /// <param name="format">The format specifier, one of the
        /// "Long Date", "Short Date", "Long Time", "Short Time" values.</param>
        /// <returns>The formatted string.</returns>
        public static string FormatDateTime (DateTime value, string format)
        {
            var _format = format.ToLower().Replace (" ", "");
            switch (_format)
            {
                case "longdate":
                    return value.ToString ("D", null);
                case "shortdate":
                    return value.ToString ("d", null);
                case "longtime":
                    return value.ToString ("T", null);
                case "shorttime":
                    return value.ToString ("HH:mm", null);
            }

            return value.ToString (format, null);
        }

        /// <summary>
        /// Returns a string formatted as a numeric value.
        /// </summary>
        /// <param name="value">The value to format.</param>
        /// <returns>The formatted string.</returns>
        public static string FormatNumber (object value)
        {
            if (value is Variant variant)
            {
                value = variant.Value;
            }

            return string.Format ("{0:n}", value);
        }

        /// <summary>
        /// Returns a string formatted as a numeric value with specified number of decimal digits.
        /// </summary>
        /// <param name="value">The value to format.</param>
        /// <param name="decimalDigits">Number of decimal digits.</param>
        /// <returns>The formatted string.</returns>
        public static string FormatNumber (object value, int decimalDigits)
        {
            if (value is Variant variant)
            {
                value = variant.Value;
            }

            var info = CultureInfo.CurrentCulture.NumberFormat.Clone() as NumberFormatInfo;
            info.NumberDecimalDigits = decimalDigits;
            return string.Format (info, "{0:n}", value);
        }

        /// <summary>
        /// Returns a string formatted as a percent value.
        /// </summary>
        /// <param name="value">The value to format.</param>
        /// <returns>The formatted string.</returns>
        public static string FormatPercent (object value)
        {
            if (value is Variant variant)
            {
                value = variant.Value;
            }

            return string.Format ("{0:p}", value);
        }

        /// <summary>
        /// Returns a string formatted as a percent value with specified number of decimal digits.
        /// </summary>
        /// <param name="value">The value to format.</param>
        /// <param name="decimalDigits">Number of decimal digits.</param>
        /// <returns>The formatted string.</returns>
        public static string FormatPercent (object value, int decimalDigits)
        {
            if (value is Variant variant)
            {
                value = variant.Value;
            }

            var info = CultureInfo.CurrentCulture.NumberFormat.Clone() as NumberFormatInfo;
            info.PercentDecimalDigits = decimalDigits;
            return string.Format (info, "{0:p}", value);
        }

        #endregion

        #region Conversion

        /// <summary>
        /// Converts a numeric value to Roman string representation.
        /// </summary>
        /// <param name="value">Integer value in range 0-3998.</param>
        /// <returns>The string in Roman form.</returns>
        public static string ToRoman (object value)
        {
            return Roman.Convert (Convert.ToInt32 (value));
        }

        /// <summary>
        /// Converts a currency value to an english (US) string representation of that value.
        /// </summary>
        /// <param name="value">The currency value to convert.</param>
        /// <returns>The string representation of the specified value.</returns>
        public static string ToWords (object value)
        {
            return ToWords (value, "USD");
        }

        /// <summary>
        /// Converts a currency value to an english (US) string representation of that value,
        /// using the specified currency.
        /// </summary>
        /// <param name="value">The currency value to convert.</param>
        /// <param name="currencyName">The 3-digit ISO name of the currency, for example "EUR".</param>
        /// <returns>The string representation of the specified value.</returns>
        public static string ToWords (object value, string currencyName)
        {
            return new NumToWordsEn().ConvertCurrency (Convert.ToDecimal (value), currencyName);
        }

        /// <summary>
        /// Converts a numeric value to an english (US) string representation of that value.
        /// </summary>
        /// <param name="value">The numeric value to convert.</param>
        /// <param name="one">The name in singular form, for example "page".</param>
        /// <param name="many">The name in plural form, for example "pages".</param>
        /// <returns>The string representation of the specified value.</returns>
        public static string ToWords (object value, string one, string many)
        {
            return new NumToWordsEn().ConvertNumber (Convert.ToDecimal (value), true, one, many, many);
        }

        /// <summary>
        /// Converts a currency value to an english (GB) string representation of that value.
        /// </summary>
        /// <param name="value">The currency value to convert.</param>
        /// <returns>The string representation of the specified value.</returns>
        public static string ToWordsEnGb (object value)
        {
            return ToWordsEnGb (value, "GBP");
        }

        /// <summary>
        /// Converts a currency value to an english (GB) string representation of that value,
        /// using the specified currency.
        /// </summary>
        /// <param name="value">The currency value to convert.</param>
        /// <param name="currencyName">The 3-digit ISO name of the currency, for example "EUR".</param>
        /// <returns>The string representation of the specified value.</returns>
        public static string ToWordsEnGb (object value, string currencyName)
        {
            return new NumToWordsEnGb().ConvertCurrency (Convert.ToDecimal (value), currencyName);
        }

        /// <summary>
        /// Converts a numeric value to an english (GB) string representation of that value.
        /// </summary>
        /// <param name="value">The numeric value to convert.</param>
        /// <param name="one">The name in singular form, for example "page".</param>
        /// <param name="many">The name in plural form, for example "pages".</param>
        /// <returns>The string representation of the specified value.</returns>
        public static string ToWordsEnGb (object value, string one, string many)
        {
            return new NumToWordsEnGb().ConvertNumber (Convert.ToDecimal (value), true, one, many, many);
        }

        /// <summary>
        /// Converts a currency value to a spanish string representation of that value.
        /// </summary>
        /// <param name="value">The currency value to convert.</param>
        /// <returns>The string representation of the specified value.</returns>
        public static string ToWordsEs (object value)
        {
            return ToWordsEs (value, "EUR");
        }

        /// <summary>
        /// Converts a currency value to a spanish string representation of that value,
        /// using the specified currency.
        /// </summary>
        /// <param name="value">The currency value to convert.</param>
        /// <param name="currencyName">The 3-digit ISO name of the currency, for example "EUR".</param>
        /// <returns>The string representation of the specified value.</returns>
        public static string ToWordsEs (object value, string currencyName)
        {
            return new NumToWordsEs().ConvertCurrency (Convert.ToDecimal (value), currencyName);
        }

        /// <summary>
        /// Converts a numeric value to a spanish string representation of that value.
        /// </summary>
        /// <param name="value">The numeric value to convert.</param>
        /// <param name="one">The name in singular form, for example "page".</param>
        /// <param name="many">The name in plural form, for example "pages".</param>
        /// <returns>The string representation of the specified value.</returns>
        public static string ToWordsEs (object value, string one, string many)
        {
            return new NumToWordsEs().ConvertNumber (Convert.ToDecimal (value), true, one, many, many);
        }

        /// <summary>
        /// Converts a currency value to a russian string representation of that value.
        /// </summary>
        /// <param name="value">The currency value to convert.</param>
        /// <returns>The string representation of the specified value.</returns>
        public static string ToWordsRu (object value)
        {
            return ToWordsRu (value, "RUR");
        }

        /// <summary>
        /// Converts a currency value to a russian string representation of that value,
        /// using the specified currency.
        /// </summary>
        /// <param name="value">The currency value to convert.</param>
        /// <param name="currencyName">The 3-digit ISO name of the currency, for example "EUR".</param>
        /// <returns>The string representation of the specified value.</returns>
        public static string ToWordsRu (object value, string currencyName)
        {
            return new NumToWordsRu().ConvertCurrency (Convert.ToDecimal (value), currencyName);
        }

        /// <summary>
        /// Converts a numeric value to a russian string representation of that value.
        /// </summary>
        /// <param name="value">The numeric value to convert.</param>
        /// <param name="male">True if the name is of male gender.</param>
        /// <param name="one">The name in singular form, for example "страница".</param>
        /// <param name="two">The name in plural form, for example "страницы".</param>
        /// <param name="many">The name in plural form, for example "страниц".</param>
        /// <returns>The string representation of the specified value.</returns>
        public static string ToWordsRu (object value, bool male, string one, string two, string many)
        {
            return new NumToWordsRu().ConvertNumber (Convert.ToDecimal (value), male, one, two, many);
        }

        /// <summary>
        /// Converts a currency value to a german string representation of that value.
        /// </summary>
        /// <param name="value">The currency value to convert.</param>
        /// <returns>The string representation of the specified value.</returns>
        public static string ToWordsDe (object value)
        {
            return ToWordsDe (value, "EUR");
        }

        /// <summary>
        /// Converts a currency value to a german string representation of that value,
        /// using the specified currency.
        /// </summary>
        /// <param name="value">The currency value to convert.</param>
        /// <param name="currencyName">The 3-digit ISO name of the currency, for example "EUR".</param>
        /// <returns>The string representation of the specified value.</returns>
        public static string ToWordsDe (object value, string currencyName)
        {
            return new NumToWordsDe().ConvertCurrency (Convert.ToDecimal (value), currencyName);
        }

        /// <summary>
        /// Converts a numeric value to a german string representation of that value.
        /// </summary>
        /// <param name="value">The numeric value to convert.</param>
        /// <param name="one">The name in singular form, for example "page".</param>
        /// <param name="many">The name in plural form, for example "pages".</param>
        /// <returns>The string representation of the specified value.</returns>
        public static string ToWordsDe (object value, string one, string many)
        {
            return new NumToWordsDe().ConvertNumber (Convert.ToDecimal (value), true, one, many, many);
        }

        /// <summary>
        /// Converts a currency value to a french string representation of that value.
        /// </summary>
        /// <param name="value">The currency value to convert.</param>
        /// <returns>The string representation of the specified value.</returns>
        public static string ToWordsFr (object value)
        {
            return ToWordsFr (value, "EUR");
        }

        /// <summary>
        /// Converts a currency value to a french string representation of that value,
        /// using the specified currency.
        /// </summary>
        /// <param name="value">The currency value to convert.</param>
        /// <param name="currencyName">The 3-digit ISO name of the currency, for example "EUR".</param>
        /// <returns>The string representation of the specified value.</returns>
        public static string ToWordsFr (object value, string currencyName)
        {
            return new NumToWordsFr().ConvertCurrency (Convert.ToDecimal (value), currencyName);
        }

        /// <summary>
        /// Converts a numeric value to a french string representation of that value.
        /// </summary>
        /// <param name="value">The numeric value to convert.</param>
        /// <param name="one">The name in singular form, for example "page".</param>
        /// <param name="many">The name in plural form, for example "pages".</param>
        /// <returns>The string representation of the specified value.</returns>
        public static string ToWordsFr (object value, string one, string many)
        {
            return new NumToWordsFr().ConvertNumber (Convert.ToDecimal (value), true, one, many, many);
        }

        /// <summary>
        /// Converts a currency value to a dutch string representation of that value.
        /// </summary>
        /// <param name="value">The currency value to convert.</param>
        /// <returns>The string representation of the specified value.</returns>
        public static string ToWordsNl (object value)
        {
            return ToWordsNl (value, "EUR");
        }

        /// <summary>
        /// Converts a currency value to a dutch string representation of that value,
        /// using the specified currency.
        /// </summary>
        /// <param name="value">The currency value to convert.</param>
        /// <param name="currencyName">The 3-digit ISO name of the currency, for example "EUR".</param>
        /// <returns>The string representation of the specified value.</returns>
        public static string ToWordsNl (object value, string currencyName)
        {
            return new NumToWordsNl().ConvertCurrency (Convert.ToDecimal (value), currencyName);
        }

        /// <summary>
        /// Converts a numeric value to a dutch string representation of that value.
        /// </summary>
        /// <param name="value">The numeric value to convert.</param>
        /// <param name="one">The name in singular form, for example "page".</param>
        /// <param name="many">The name in plural form, for example "pages".</param>
        /// <returns>The string representation of the specified value.</returns>
        public static string ToWordsNl (object value, string one, string many)
        {
            return new NumToWordsNl().ConvertNumber (Convert.ToDecimal (value), true, one, many, many);
        }

        /// <summary>
        /// Converts a numeric value to a indian numbering system string representation of that value.
        /// </summary>
        /// <param name="value">the currency value to convert</param>
        /// <returns>The string representation of the specified value.</returns>
        public static string ToWordsIn (object value)
        {
            return ToWordsIn (value, "INR");
        }

        /// <summary>
        /// Converts a numeric value to a indian numbering system string representation of that value.
        /// </summary>
        /// <param name="value">he numeric value to convert.</param>
        /// <param name="currencyName">The 3-digit ISO name of the currency, for example "INR".</param>
        /// <returns></returns>
        public static string ToWordsIn (object value, string currencyName)
        {
            return new NumToWordsIn().ConvertCurrency (Convert.ToDecimal (value), currencyName);
        }

        /// <summary>
        /// Converts a numeric value to a indian numbering system string representation of that value.
        /// </summary>
        /// <param name="value">The numeric value to convert.</param>
        /// <param name="one">The name in singular form, for example "page".</param>
        /// <param name="many">The name in plural form, for example "pages".</param>
        /// <returns>The string representation of the specified value.</returns>
        public static string ToWordsIn (object value, string one, string many)
        {
            return new NumToWordsIn().ConvertNumber (Convert.ToDecimal (value), true, one, many, many);
        }

        /// <summary>
        /// Converts a numeric value to a ukrainian string representation of that value.
        /// </summary>
        /// <param name="value">The numeric value to convert.</param>
        /// <returns>The string representation of the specified value.</returns>
        public static string ToWordsUkr (object value)
        {
            return ToWordsUkr (value, "UAH");
        }


        /// <summary>
        /// Converts a currency value to a ukrainian string representation of that value,
        /// using the specified currency.
        /// </summary>
        /// <param name="value">The currency value to convert.</param>
        /// <param name="currencyName">The 3-digit ISO name of the currency, for example "UAH".</param>
        /// <returns>The string representation of the specified value.</returns>
        public static string ToWordsUkr (object value, string currencyName)
        {
            return new NumToWordsUkr().ConvertCurrency (Convert.ToDecimal (value), currencyName);
        }

        /// <summary>
        /// Converts a numeric value to a ukrainian string representation of that value.
        /// </summary>
        /// <param name="value">The numeric value to convert.</param>
        /// <param name="male">True if the name is of male gender.</param>
        /// <param name="one">The name in singular form, for example "сторінка".</param>
        /// <param name="two">The name in plural form, for example "сторінки".</param>
        /// <param name="many">The name in plural form, for example "сторінок".</param>
        /// <returns>The string representation of the specified value.</returns>
        public static string ToWordsUkr (object value, bool male, string one, string two, string many)
        {
            return new NumToWordsUkr().ConvertNumber (Convert.ToDecimal (value), male, one, two, many);
        }


        /// <summary>
        /// Converts a numeric value to a spanish string representation of that value.
        /// </summary>
        /// <param name="value">The numeric value to convert.</param>
        /// <returns>The string representation of the specified value.</returns>
        public static string ToWordsSp (object value)
        {
            return ToWordsSp (value, "EUR");
        }

        /// <summary>
        /// Converts a numeric value to a spanish representation of that value.
        /// </summary>
        /// <param name="value">he numeric value to convert.</param>
        /// <param name="currencyName">The 3-digit ISO name of the currency, for example "EUR".</param>
        /// <returns></returns>
        public static string ToWordsSp (object value, string currencyName)
        {
            return new NumToWordsSp().ConvertCurrency (Convert.ToDecimal (value), currencyName);
        }

        /// <summary>
        /// Converts a numeric value to a spanish string representation of that value.
        /// </summary>
        /// <param name="value">The numeric value to convert.</param>
        /// <param name="one">The name in singular form, for example "silla".</param>
        /// <param name="many">The name in plural form, for example "Sillas".</param>
        /// <returns>The string representation of the specified value.</returns>
        public static string ToWordsSp (object value, string one, string many)
        {
            return new NumToWordsSp().ConvertNumber (Convert.ToDecimal (value), true, one, many, many);
        }

        /// <summary>
        /// Converts a numeric value to a persian string representation of that value.
        /// </summary>
        /// <param name="value">The numeric value to convert.</param>
        /// <returns>The string representation of the specified value.</returns>
        public static string ToWordsPersian (object value)
        {
            return ToWordsPersian (value, "EUR");
        }

        /// <summary>
        /// Converts a numeric value to a persian representation of that value.
        /// </summary>
        /// <param name="value">he numeric value to convert.</param>
        /// <param name="currencyName">The 3-digit ISO name of the currency, for example "EUR".</param>
        /// <returns></returns>
        public static string ToWordsPersian (object value, string currencyName)
        {
            return new NumToWordsPersian().ConvertCurrency (Convert.ToDecimal (value), currencyName);
        }

        /// <summary>
        /// Converts a numeric value to a persian string representation of that value.
        /// </summary>
        /// <param name="value">The numeric value to convert.</param>
        /// <param name="one">The name in singular form, for example "silla".</param>
        /// <param name="many">The name in plural form, for example "Sillas".</param>
        /// <returns>The string representation of the specified value.</returns>
        public static string ToWordsPersian (object value, string one, string many)
        {
            return new NumToWordsPersian().ConvertNumber (Convert.ToDecimal (value), true, one, many, many);
        }

        /// <summary>
        /// Converts a numeric value to a polish string representation of that value.
        /// </summary>
        /// <param name="value">The numeric value to convert.</param>
        /// <returns>The string representation of the specified value.</returns>
        public static string ToWordsPl (object value)
        {
            return ToWordsPl (value, "PLN");
        }

        /// <summary>
        /// Converts a numeric value to a polish representation of that value.
        /// </summary>
        /// <param name="value">he numeric value to convert.</param>
        /// <param name="currencyName">The 3-digit ISO name of the currency, for example "EUR".</param>
        /// <returns></returns>
        public static string ToWordsPl (object value, string currencyName)
        {
            return new NumToWordsPl().ConvertCurrency (Convert.ToDecimal (value), currencyName);
        }

        /// <summary>
        /// Converts a numeric value to a polish string representation of that value.
        /// </summary>
        /// <param name="value">The numeric value to convert.</param>
        /// <param name="one">The name in singular form, for example "silla".</param>
        /// <param name="many">The name in plural form, for example "Sillas".</param>
        /// <returns>The string representation of the specified value.</returns>
        public static string ToWordsPl (object value, string one, string many)
        {
            return new NumToWordsPl().ConvertNumber (Convert.ToDecimal (value), true, one, many, many);
        }

        /// <summary>
        /// Converts a value to an english (US) alphabet string representation of that value.
        /// </summary>
        /// <param name="value">The value to convert.</param>
        /// <returns>The alphabet string representation of the specified value.</returns>
        public static string ToLetters (object value)
        {
            return ToLetters (value, false);
        }

        /// <summary>
        /// Converts a value to an english (US) alphabet string representation of that value.
        /// </summary>
        /// <param name="value">The value to convert.</param>
        /// <param name="isUpper">Bool indicating that letters should be in upper registry.</param>
        /// <returns>The alphabet string representation of the specified value.</returns>
        public static string ToLetters (object value, bool isUpper)
        {
            return new NumToLettersEn().ConvertNumber (Convert.ToInt32 (value), isUpper);
        }

        /// <summary>
        /// Converts a value to a russian alphabet string representation of that value.
        /// </summary>
        /// <param name="value">The value to convert.</param>
        /// <returns>The alphabet string representation of the specified value.</returns>
        public static string ToLettersRu (object value)
        {
            return ToLettersRu (value, false);
        }

        /// <summary>
        /// Converts a value to a russian alphabet string representation of that value.
        /// </summary>
        /// <param name="value">The value to convert.</param>
        /// <param name="isUpper">Bool indicating that letters should be in upper registry.</param>
        /// <returns>The alphabet string representation of the specified value.</returns>
        public static string ToLettersRu (object value, bool isUpper)
        {
            return new NumToLettersRu().ConvertNumber (Convert.ToInt32 (value), isUpper);
        }

        #endregion

        #region Program Flow

        /// <summary>
        /// Selects and returns a value from a list of arguments.
        /// </summary>
        /// <param name="index">A value between 1 and the number of elements passed in the "choice" argument.</param>
        /// <param name="choice">Object parameter array.</param>
        /// <returns>One of the values in the "choice" argument.</returns>
        public static object Choose (double index, params object[] choice)
        {
            var ind = (int)index - 1;
            if (ind < 0 || ind >= choice.Length)
            {
                return null;
            }

            return choice[ind];
        }

        /// <summary>
        /// Returns one of two objects, depending on the evaluation of an expression.
        /// </summary>
        /// <param name="expression">The expression you want to evaluate.</param>
        /// <param name="truePart">Returned if Expression evaluates to True.</param>
        /// <param name="falsePart">Returned if Expression evaluates to False.</param>
        /// <returns>Either truePart os falsePart.</returns>
        public static object IIf (bool expression, object truePart, object falsePart)
        {
            return expression ? truePart : falsePart;
        }

        /// <summary>
        /// Evaluates a list of expressions and returns a value corresponding to the first
        /// expression in the list that is True.
        /// </summary>
        /// <param name="expressions">Parameter array consists of paired expressions and values.</param>
        /// <returns>The value corresponding to an expression which returns true.</returns>
        public static object Switch (params object[] expressions)
        {
            for (var i = 0; i + 1 < expressions.Length; i += 2)
            {
                if (Convert.ToBoolean (expressions[i]) == true)
                {
                    return expressions[i + 1];
                }
            }

            return null;
        }

        /// <summary>
        /// Checks if the specified object is null.
        /// </summary>
        /// <param name="thisReport">The report instance.</param>
        /// <param name="name">Either a name of DB column, or a parameter name, or a total name to check.</param>
        /// <returns><b>true</b> if the object's value is null.</returns>
        public static bool IsNull (Report thisReport, string name)
        {
            object value = null;
            if (DataHelper.IsValidColumn (thisReport.Dictionary, name))
            {
                value = thisReport.GetColumnValueNullable (name);
            }
            else if (DataHelper.IsValidParameter (thisReport.Dictionary, name))
            {
                value = thisReport.GetParameterValue (name);
            }
            else if (DataHelper.IsValidTotal (thisReport.Dictionary, name))
            {
                value = thisReport.GetTotalValueNullable (name).Value;
            }

            return value == null || value == DBNull.Value;
        }

        #endregion

        internal static void Register()
        {
            #region Math

            RegisteredObjects.AddFunctionCategory ("Math", "Functions,Math");
            var math = typeof (Math);
            RegisteredObjects.AddFunction (math.GetMethod ("Abs", new Type[] { typeof (sbyte) }), "Math,Abs");
            RegisteredObjects.InternalAddFunction (math.GetMethod ("Abs", new Type[] { typeof (short) }), "Math,Abs");
            RegisteredObjects.InternalAddFunction (math.GetMethod ("Abs", new Type[] { typeof (int) }), "Math,Abs");
            RegisteredObjects.InternalAddFunction (math.GetMethod ("Abs", new Type[] { typeof (long) }), "Math,Abs");
            RegisteredObjects.InternalAddFunction (math.GetMethod ("Abs", new Type[] { typeof (float) }), "Math,Abs");
            RegisteredObjects.InternalAddFunction (math.GetMethod ("Abs", new Type[] { typeof (double) }), "Math,Abs");
            RegisteredObjects.InternalAddFunction (math.GetMethod ("Abs", new Type[] { typeof (decimal) }), "Math,Abs");
            RegisteredObjects.InternalAddFunction (math.GetMethod ("Acos"), "Math");
            RegisteredObjects.InternalAddFunction (math.GetMethod ("Asin"), "Math");
            RegisteredObjects.InternalAddFunction (math.GetMethod ("Atan"), "Math");
            RegisteredObjects.InternalAddFunction (math.GetMethod ("Ceiling", new Type[] { typeof (double) }),
                "Math,Ceiling");
            RegisteredObjects.InternalAddFunction (math.GetMethod ("Ceiling", new Type[] { typeof (decimal) }),
                "Math,Ceiling");
            RegisteredObjects.InternalAddFunction (math.GetMethod ("Cos"), "Math");
            RegisteredObjects.InternalAddFunction (math.GetMethod ("Exp"), "Math");
            RegisteredObjects.InternalAddFunction (math.GetMethod ("Floor", new Type[] { typeof (double) }),
                "Math,Floor");
            RegisteredObjects.InternalAddFunction (math.GetMethod ("Floor", new Type[] { typeof (decimal) }),
                "Math,Floor");
            RegisteredObjects.InternalAddFunction (math.GetMethod ("Log", new Type[] { typeof (double) }), "Math");
            var myMath = typeof (StdFunctions);
            RegisteredObjects.InternalAddFunction (
                myMath.GetMethod ("Maximum", new Type[] { typeof (int), typeof (int) }), "Math,Maximum");
            RegisteredObjects.InternalAddFunction (
                myMath.GetMethod ("Maximum", new Type[] { typeof (long), typeof (long) }), "Math,Maximum");
            RegisteredObjects.InternalAddFunction (
                myMath.GetMethod ("Maximum", new Type[] { typeof (float), typeof (float) }), "Math,Maximum");
            RegisteredObjects.InternalAddFunction (
                myMath.GetMethod ("Maximum", new Type[] { typeof (double), typeof (double) }), "Math,Maximum");
            RegisteredObjects.InternalAddFunction (
                myMath.GetMethod ("Maximum", new Type[] { typeof (decimal), typeof (decimal) }), "Math,Maximum");
            RegisteredObjects.InternalAddFunction (
                myMath.GetMethod ("Minimum", new Type[] { typeof (int), typeof (int) }), "Math,Minimum");
            RegisteredObjects.InternalAddFunction (
                myMath.GetMethod ("Minimum", new Type[] { typeof (long), typeof (long) }), "Math,Minimum");
            RegisteredObjects.InternalAddFunction (
                myMath.GetMethod ("Minimum", new Type[] { typeof (float), typeof (float) }), "Math,Minimum");
            RegisteredObjects.InternalAddFunction (
                myMath.GetMethod ("Minimum", new Type[] { typeof (double), typeof (double) }), "Math,Minimum");
            RegisteredObjects.InternalAddFunction (
                myMath.GetMethod ("Minimum", new Type[] { typeof (decimal), typeof (decimal) }), "Math,Minimum");
            RegisteredObjects.InternalAddFunction (math.GetMethod ("Round", new Type[] { typeof (double) }),
                "Math,Round");
            RegisteredObjects.InternalAddFunction (math.GetMethod ("Round", new Type[] { typeof (decimal) }),
                "Math,Round");
            RegisteredObjects.InternalAddFunction (
                math.GetMethod ("Round", new Type[] { typeof (double), typeof (int) }), "Math,Round");
            RegisteredObjects.InternalAddFunction (
                math.GetMethod ("Round", new Type[] { typeof (decimal), typeof (int) }), "Math,Round");
            RegisteredObjects.InternalAddFunction (math.GetMethod ("Sin"), "Math");
            RegisteredObjects.InternalAddFunction (math.GetMethod ("Sqrt"), "Math");
            RegisteredObjects.InternalAddFunction (math.GetMethod ("Tan"), "Math");
            RegisteredObjects.InternalAddFunction (math.GetMethod ("Truncate", new Type[] { typeof (double) }),
                "Math,Truncate");
            RegisteredObjects.InternalAddFunction (math.GetMethod ("Truncate", new Type[] { typeof (decimal) }),
                "Math,Truncate");

            #endregion

            #region Text

            RegisteredObjects.AddFunctionCategory ("Text", "Functions,Text");
            var str = typeof (StdFunctions);
            RegisteredObjects.InternalAddFunction (str.GetMethod ("Asc"), "Text");
            RegisteredObjects.InternalAddFunction (str.GetMethod ("Chr"), "Text");
            RegisteredObjects.InternalAddFunction (str.GetMethod ("Insert"), "Text");
            RegisteredObjects.InternalAddFunction (str.GetMethod ("Length"), "Text");
            RegisteredObjects.InternalAddFunction (str.GetMethod ("LowerCase"), "Text");
            RegisteredObjects.InternalAddFunction (
                str.GetMethod ("PadLeft", new Type[] { typeof (string), typeof (int) }), "Text,PadLeft");
            RegisteredObjects.InternalAddFunction (
                str.GetMethod ("PadLeft", new Type[] { typeof (string), typeof (int), typeof (char) }), "Text,PadLeft");
            RegisteredObjects.InternalAddFunction (
                str.GetMethod ("PadRight", new Type[] { typeof (string), typeof (int) }), "Text,PadRight");
            RegisteredObjects.InternalAddFunction (
                str.GetMethod ("PadRight", new Type[] { typeof (string), typeof (int), typeof (char) }),
                "Text,PadRight");
            RegisteredObjects.InternalAddFunction (
                str.GetMethod ("Remove", new Type[] { typeof (string), typeof (int) }), "Text,Remove");
            RegisteredObjects.InternalAddFunction (
                str.GetMethod ("Remove", new Type[] { typeof (string), typeof (int), typeof (int) }), "Text,Remove");
            RegisteredObjects.InternalAddFunction (str.GetMethod ("Replace"), "Text");
            RegisteredObjects.InternalAddFunction (
                str.GetMethod ("Substring", new Type[] { typeof (string), typeof (int) }), "Text,Substring");
            RegisteredObjects.InternalAddFunction (
                str.GetMethod ("Substring", new Type[] { typeof (string), typeof (int), typeof (int) }),
                "Text,Substring");
            RegisteredObjects.InternalAddFunction (str.GetMethod ("TitleCase"), "Text");
            RegisteredObjects.InternalAddFunction (str.GetMethod ("Trim"), "Text");
            RegisteredObjects.InternalAddFunction (str.GetMethod ("UpperCase"), "Text");

            #endregion

            #region Date & Time

            RegisteredObjects.AddFunctionCategory ("DateTime", "Functions,DateTime");
            var dt = typeof (StdFunctions);
            RegisteredObjects.InternalAddFunction (dt.GetMethod ("AddDays"), "DateTime");
            RegisteredObjects.InternalAddFunction (dt.GetMethod ("AddHours"), "DateTime");
            RegisteredObjects.InternalAddFunction (dt.GetMethod ("AddMinutes"), "DateTime");
            RegisteredObjects.InternalAddFunction (dt.GetMethod ("AddMonths"), "DateTime");
            RegisteredObjects.InternalAddFunction (dt.GetMethod ("AddSeconds"), "DateTime");
            RegisteredObjects.InternalAddFunction (dt.GetMethod ("AddYears"), "DateTime");
            RegisteredObjects.InternalAddFunction (dt.GetMethod ("DateDiff"), "DateTime");
            RegisteredObjects.InternalAddFunction (dt.GetMethod ("DateSerial"), "DateTime");
            RegisteredObjects.InternalAddFunction (dt.GetMethod ("Day"), "DateTime");
            RegisteredObjects.InternalAddFunction (dt.GetMethod ("DayOfWeek"), "DateTime");
            RegisteredObjects.InternalAddFunction (dt.GetMethod ("DayOfYear"), "DateTime");
            RegisteredObjects.InternalAddFunction (dt.GetMethod ("DaysInMonth"), "DateTime");
            RegisteredObjects.InternalAddFunction (dt.GetMethod ("Hour"), "DateTime");
            RegisteredObjects.InternalAddFunction (dt.GetMethod ("Minute"), "DateTime");
            RegisteredObjects.InternalAddFunction (dt.GetMethod ("Month"), "DateTime");
            RegisteredObjects.InternalAddFunction (dt.GetMethod ("MonthName"), "DateTime");
            RegisteredObjects.InternalAddFunction (dt.GetMethod ("Second"), "DateTime");
            RegisteredObjects.InternalAddFunction (dt.GetMethod ("WeekOfYear"), "DateTime");
            RegisteredObjects.InternalAddFunction (dt.GetMethod ("Year"), "DateTime");

            #endregion

            #region Formatting

            RegisteredObjects.AddFunctionCategory ("Formatting", "Functions,Formatting");
            var fmt = typeof (StdFunctions);
            RegisteredObjects.InternalAddFunction (
                fmt.GetMethod ("Format", new Type[] { typeof (string), typeof (object[]) }), "Formatting");
            RegisteredObjects.InternalAddFunction (fmt.GetMethod ("FormatCurrency", new Type[] { typeof (object) }),
                "Formatting,FormatCurrency");
            RegisteredObjects.InternalAddFunction (
                fmt.GetMethod ("FormatCurrency", new Type[] { typeof (object), typeof (int) }),
                "Formatting,FormatCurrency");
            RegisteredObjects.InternalAddFunction (fmt.GetMethod ("FormatDateTime", new Type[] { typeof (DateTime) }),
                "Formatting,FormatDateTime");
            RegisteredObjects.InternalAddFunction (
                fmt.GetMethod ("FormatDateTime", new Type[] { typeof (DateTime), typeof (string) }),
                "Formatting,FormatDateTime");
            RegisteredObjects.InternalAddFunction (fmt.GetMethod ("FormatNumber", new Type[] { typeof (object) }),
                "Formatting,FormatNumber");
            RegisteredObjects.InternalAddFunction (
                fmt.GetMethod ("FormatNumber", new Type[] { typeof (object), typeof (int) }),
                "Formatting,FormatNumber");
            RegisteredObjects.InternalAddFunction (fmt.GetMethod ("FormatPercent", new Type[] { typeof (object) }),
                "Formatting,FormatPercent");
            RegisteredObjects.InternalAddFunction (
                fmt.GetMethod ("FormatPercent", new Type[] { typeof (object), typeof (int) }),
                "Formatting,FormatPercent");

            #endregion

            #region Conversion

            RegisteredObjects.AddFunctionCategory ("Conversion", "Functions,Conversion");
            var stdConv = typeof (Convert);
            var myConv = typeof (StdFunctions);
            RegisteredObjects.InternalAddFunction (stdConv.GetMethod ("ToBoolean", new Type[] { typeof (object) }),
                "Conversion");
            RegisteredObjects.InternalAddFunction (stdConv.GetMethod ("ToByte", new Type[] { typeof (object) }),
                "Conversion");
            RegisteredObjects.InternalAddFunction (stdConv.GetMethod ("ToChar", new Type[] { typeof (object) }),
                "Conversion");
            RegisteredObjects.InternalAddFunction (stdConv.GetMethod ("ToDateTime", new Type[] { typeof (object) }),
                "Conversion");
            RegisteredObjects.InternalAddFunction (stdConv.GetMethod ("ToDecimal", new Type[] { typeof (object) }),
                "Conversion");
            RegisteredObjects.InternalAddFunction (stdConv.GetMethod ("ToDouble", new Type[] { typeof (object) }),
                "Conversion");
            RegisteredObjects.InternalAddFunction (stdConv.GetMethod ("ToInt32", new Type[] { typeof (object) }),
                "Conversion");
            RegisteredObjects.InternalAddFunction (myConv.GetMethod ("ToRoman"), "Conversion");
            RegisteredObjects.InternalAddFunction (stdConv.GetMethod ("ToSingle", new Type[] { typeof (object) }),
                "Conversion");
            RegisteredObjects.InternalAddFunction (stdConv.GetMethod ("ToString", new Type[] { typeof (object) }),
                "Conversion");
            RegisteredObjects.InternalAddFunction (myConv.GetMethod ("ToWords", new Type[] { typeof (object) }),
                "Conversion,ToWords");
            RegisteredObjects.InternalAddFunction (
                myConv.GetMethod ("ToWords", new Type[] { typeof (object), typeof (string) }), "Conversion,ToWords");
            RegisteredObjects.InternalAddFunction (
                myConv.GetMethod ("ToWords", new Type[] { typeof (object), typeof (string), typeof (string) }),
                "Conversion,ToWords");
            RegisteredObjects.InternalAddFunction (myConv.GetMethod ("ToWordsIn", new Type[] { typeof (object) }),
                "Conversion,ToWordsIn");
            RegisteredObjects.InternalAddFunction (
                myConv.GetMethod ("ToWordsIn", new Type[] { typeof (object), typeof (string) }),
                "Conversion,ToWordsIn");
            RegisteredObjects.InternalAddFunction (
                myConv.GetMethod ("ToWordsIn", new Type[] { typeof (object), typeof (string), typeof (string) }),
                "Conversion,ToWordsIn");
            RegisteredObjects.InternalAddFunction (myConv.GetMethod ("ToWordsDe", new Type[] { typeof (object) }),
                "Conversion,ToWordsDe");
            RegisteredObjects.InternalAddFunction (
                myConv.GetMethod ("ToWordsDe", new Type[] { typeof (object), typeof (string) }),
                "Conversion,ToWordsDe");
            RegisteredObjects.InternalAddFunction (
                myConv.GetMethod ("ToWordsDe", new Type[] { typeof (object), typeof (string), typeof (string) }),
                "Conversion,ToWordsDe");
            RegisteredObjects.InternalAddFunction (myConv.GetMethod ("ToWordsEnGb", new Type[] { typeof (object) }),
                "Conversion,ToWordsEnGb");
            RegisteredObjects.InternalAddFunction (
                myConv.GetMethod ("ToWordsEnGb", new Type[] { typeof (object), typeof (string) }),
                "Conversion,ToWordsEnGb");
            RegisteredObjects.InternalAddFunction (
                myConv.GetMethod ("ToWordsEnGb", new Type[] { typeof (object), typeof (string), typeof (string) }),
                "Conversion,ToWordsEnGb");
            RegisteredObjects.InternalAddFunction (myConv.GetMethod ("ToWordsEs", new Type[] { typeof (object) }),
                "Conversion,ToWordsEs");
            RegisteredObjects.InternalAddFunction (
                myConv.GetMethod ("ToWordsEs", new Type[] { typeof (object), typeof (string) }),
                "Conversion,ToWordsEs");
            RegisteredObjects.InternalAddFunction (
                myConv.GetMethod ("ToWordsEs", new Type[] { typeof (object), typeof (string), typeof (string) }),
                "Conversion,ToWordsEs");
            RegisteredObjects.InternalAddFunction (myConv.GetMethod ("ToWordsFr", new Type[] { typeof (object) }),
                "Conversion,ToWordsFr");
            RegisteredObjects.InternalAddFunction (
                myConv.GetMethod ("ToWordsFr", new Type[] { typeof (object), typeof (string) }),
                "Conversion,ToWordsFr");
            RegisteredObjects.InternalAddFunction (
                myConv.GetMethod ("ToWordsFr", new Type[] { typeof (object), typeof (string), typeof (string) }),
                "Conversion,ToWordsFr");
            RegisteredObjects.InternalAddFunction (myConv.GetMethod ("ToWordsNl", new Type[] { typeof (object) }),
                "Conversion,ToWordsNl");
            RegisteredObjects.InternalAddFunction (
                myConv.GetMethod ("ToWordsNl", new Type[] { typeof (object), typeof (string) }),
                "Conversion,ToWordsNl");
            RegisteredObjects.InternalAddFunction (
                myConv.GetMethod ("ToWordsNl", new Type[] { typeof (object), typeof (string), typeof (string) }),
                "Conversion,ToWordsNl");
            RegisteredObjects.InternalAddFunction (myConv.GetMethod ("ToWordsRu", new Type[] { typeof (object) }),
                "Conversion,ToWordsRu");
            RegisteredObjects.InternalAddFunction (
                myConv.GetMethod ("ToWordsRu", new Type[] { typeof (object), typeof (string) }),
                "Conversion,ToWordsRu");
            RegisteredObjects.InternalAddFunction (
                myConv.GetMethod ("ToWordsRu",
                    new Type[] { typeof (object), typeof (bool), typeof (string), typeof (string), typeof (string) }),
                "Conversion,ToWordsRu");
            RegisteredObjects.InternalAddFunction (myConv.GetMethod ("ToWordsUkr", new Type[] { typeof (object) }),
                "Conversion,ToWordsUkr");
            RegisteredObjects.InternalAddFunction (
                myConv.GetMethod ("ToWordsUkr", new Type[] { typeof (object), typeof (string) }),
                "Conversion,ToWordsUkr");
            RegisteredObjects.InternalAddFunction (
                myConv.GetMethod ("ToWordsUkr",
                    new Type[] { typeof (object), typeof (bool), typeof (string), typeof (string), typeof (string) }),
                "Conversion,ToWordsUkr");
            RegisteredObjects.InternalAddFunction (
                myConv.GetMethod ("ToWordsSp", new Type[] { typeof (object), typeof (string) }),
                "Conversion,ToWordsSp");
            RegisteredObjects.InternalAddFunction (myConv.GetMethod ("ToWordsSp", new Type[] { typeof (object) }),
                "Conversion,ToWordsSp");
            RegisteredObjects.InternalAddFunction (
                myConv.GetMethod ("ToWordsSp", new Type[] { typeof (object), typeof (string), typeof (string) }),
                "Conversion,ToWordsSp");
            RegisteredObjects.InternalAddFunction (
                myConv.GetMethod ("ToWordsPersian", new Type[] { typeof (object), typeof (string) }),
                "Conversion,ToWordsPersian");
            RegisteredObjects.InternalAddFunction (myConv.GetMethod ("ToWordsPersian", new Type[] { typeof (object) }),
                "Conversion,ToWordsPersian");
            RegisteredObjects.InternalAddFunction (
                myConv.GetMethod ("ToWordsPersian", new Type[] { typeof (object), typeof (string), typeof (string) }),
                "Conversion,ToWordsPersian");
            RegisteredObjects.InternalAddFunction (myConv.GetMethod ("ToLetters", new Type[] { typeof (object) }),
                "Conversion,ToLetters");
            RegisteredObjects.InternalAddFunction (
                myConv.GetMethod ("ToLetters", new Type[] { typeof (object), typeof (bool) }), "Conversion,ToLetters");
            RegisteredObjects.InternalAddFunction (myConv.GetMethod ("ToLettersRu", new Type[] { typeof (object) }),
                "Conversion,ToLettersRu");
            RegisteredObjects.InternalAddFunction (
                myConv.GetMethod ("ToLettersRu", new Type[] { typeof (object), typeof (bool) }),
                "Conversion,ToLettersRu");
            RegisteredObjects.InternalAddFunction (
                myConv.GetMethod ("ToWordsPl", new Type[] { typeof (object), typeof (string) }),
                "Conversion,ToWordsPl");
            RegisteredObjects.InternalAddFunction (myConv.GetMethod ("ToWordsPl", new Type[] { typeof (object) }),
                "Conversion,ToWordsPl");
            RegisteredObjects.InternalAddFunction (
                myConv.GetMethod ("ToWordsPl", new Type[] { typeof (object), typeof (string), typeof (string) }),
                "Conversion,ToWordsPl");

            #endregion

            #region Program Flow

            RegisteredObjects.AddFunctionCategory ("ProgramFlow", "Functions,ProgramFlow");
            var misc = typeof (StdFunctions);
            RegisteredObjects.InternalAddFunction (misc.GetMethod ("Choose"), "ProgramFlow");
            RegisteredObjects.InternalAddFunction (misc.GetMethod ("IIf"), "ProgramFlow");
            RegisteredObjects.InternalAddFunction (misc.GetMethod ("Switch"), "ProgramFlow");
            RegisteredObjects.InternalAddFunction (misc.GetMethod ("IsNull"), "ProgramFlow");

            #endregion
        }
    }
}
